from ftplib import FTP
import os
import io


class RFtp:
    ftp = FTP()

    def __init__(self, host, port=21):
        self.ftp.connect(host, port)

    def login(self, username, pwd):
        self.ftp.set_debuglevel(0)
        self.ftp.login(username, pwd)
        print(self.ftp.welcome)

    def upload_file(self, localpath, remotepath, filename):
        cwd = os.getcwd()
        os.chdir(localpath)  # change work dir
        self.ftp.cwd(remotepath)  # remote ftp dir
        file_handle = open(filename, "rb")  # open in write mode
        self.ftp.storbinary('STOR %s' % os.path.basename(filename),
                            file_handle, blocksize=1024)  # unload file
        os.chdir(cwd)

    def download_file(self, localpath, remotepath, filename):
        cwd = os.getcwd()
        os.chdir(localpath)  # change work dir
        self.ftp.cwd(remotepath)  # remote ftp dir
        self.ftp.nlst()  # get files in remote dir
        file_handle = open(filename, "wb").write  # open in write mode
        self.ftp.retrbinary('RETR %s' % os.path.basename(filename),
                            file_handle, blocksize=1024)  # download file
        os.chdir(cwd)

    def download_file_to_buf(self, iobuf: io.BytesIO, remotepath, filename):
        self.ftp.cwd(remotepath)  # remote ftp dir
        self.ftp.nlst()  # get files in remote dir
        file_handle = iobuf.write  # open in write mode
        self.ftp.retrbinary('RETR %s' % os.path.basename(filename),
                            file_handle, blocksize=1024)  # download file

    def _is_dir(self, name):
        try:
            self.ftp.cwd(name)
        except:
            return False
        else:
            self.ftp.cwd('..')
            return True

    def _is_file(self, name):
        return not self._is_dir(name)

    def search_and_get_dir_path(self, start_path: str, dir_name: str):
        self.ftp.cwd(start_path)
        lines = []
        self.ftp.retrlines('LIST', lines.append)
        entries = []
        for li in lines:
            entries.append(li.split()[-1])
        for ent in entries:
            if ent == dir_name and self._is_dir(ent):
                return '/'.join([start_path, ent])
        # recurse to find the dir
        for ent in entries:
            if ent != '.' and ent != '..' and self._is_dir(ent):
                res = self.search_and_get_dir_path(
                    ('/'.join([start_path, ent])), dir_name)
                if res is not None:
                    return res
                else:
                    self.ftp.cwd('..')

        return None

    def get_dirlist_in_path(self, path: str):
        self.ftp.cwd(path)
        lines = []
        self.ftp.retrlines('LIST', lines.append)
        dirs = []
        for li in lines:
            ent = ' '.join(li.split()[8:])
            if ent != '.' and ent != '..' and self._is_dir(ent):
                dirs.append('/'.join([path, ent]))
        return dirs

    def get_filelist_in_path(self, path: str):
        self.ftp.cwd(path)
        lines = []
        self.ftp.retrlines('LIST', lines.append)
        files = []
        for li in lines:
            ent = ' '.join(li.split()[8:])
            if ent != '.' and ent != '..' and self._is_file(ent):
                files.append('/'.join([path, ent]))
        return files

    def is_dir_exists(self, path: str):
        pwd = self.ftp.pwd()
        try:
            self.ftp.cwd(path)
        except:
            return False
        else:
            self.ftp.cwd(pwd)
            return True

    def is_file_exists(self, file_path: str):
        a = file_path.split('/')
        d = '/'.join(a[0:len(a) - 1])
        return self.is_dir_exists(d) and \
            self.search_and_get_file_path(
            d, a[-1]
            ) is not None

    def search_and_get_file_path(self, start_path: str, file_name: str):
        self.ftp.cwd(start_path)
        lines = []
        self.ftp.retrlines('LIST', lines.append)
        entries = []
        for li in lines:
            entries.append(' '.join(li.split()[8:]))
        for ent in entries:
            if ent == file_name and self._is_file(ent):
                return '/'.join([start_path, ent])
        # recurse to find the file
        for ent in entries:
            if ent != '.' and ent != '..' and self._is_dir(ent):
                res = self.search_and_get_file_path(
                    ('/'.join([start_path, ent])), file_name)
                if res is not None:
                    return res
                else:
                    self.ftp.cwd('..')

        return None

    def close(self):
        self.ftp.set_debuglevel(0)
        self.ftp.quit()
